use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
use argon2::password_hash::{rand_core::OsRng, SaltString};
use crate::error::{AppError, AppResult};

pub struct PasswordManager {
    argon2: Argon2<'static>,
}

impl Default for PasswordManager {
    fn default() -> Self {
        Self::new()
    }
}

impl PasswordManager {
    pub fn new() -> Self {
        Self {
            argon2: Argon2::default(),
        }
    }

    pub fn hash_password(&self, password: &str) -> AppResult<String> {
        let salt = SaltString::generate(&mut OsRng);
        
        let password_hash = self.argon2
            .hash_password(password.as_bytes(), &salt)
            .map_err(|e| AppError::internal(format!("Password hashing failed: {}", e)))?
            .to_string();

        Ok(password_hash)
    }

    pub fn verify_password(&self, password: &str, hash: &str) -> AppResult<bool> {
        let parsed_hash = PasswordHash::new(hash)
            .map_err(|e| AppError::internal(format!("Invalid password hash: {}", e)))?;

        Ok(self.argon2.verify_password(password.as_bytes(), &parsed_hash).is_ok())
    }

    pub fn generate_secure_password(length: usize) -> String {
        use rand::Rng;
        const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                                abcdefghijklmnopqrstuvwxyz\
                                0123456789\
                                !@#$%^&*()_+-=[]{}|;:,.<>?";
        
        let mut rng = rand::thread_rng();
        
        (0..length)
            .map(|_| {
                let idx = rng.gen_range(0..CHARSET.len());
                CHARSET[idx] as char
            })
            .collect()
    }

    pub fn check_password_strength(password: &str) -> PasswordStrength {
        let mut score = 0;
        let mut feedback = Vec::new();

        // Length check
        if password.len() >= 8 {
            score += 1;
        } else {
            feedback.push("Password should be at least 8 characters long".to_string());
        }

        if password.len() >= 12 {
            score += 1;
        }

        // Character variety checks
        if password.chars().any(|c| c.is_ascii_lowercase()) {
            score += 1;
        } else {
            feedback.push("Password should contain lowercase letters".to_string());
        }

        if password.chars().any(|c| c.is_ascii_uppercase()) {
            score += 1;
        } else {
            feedback.push("Password should contain uppercase letters".to_string());
        }

        if password.chars().any(|c| c.is_ascii_digit()) {
            score += 1;
        } else {
            feedback.push("Password should contain numbers".to_string());
        }

        if password.chars().any(|c| "!@#$%^&*()_+-=[]{}|;:,.<>?".contains(c)) {
            score += 1;
        } else {
            feedback.push("Password should contain special characters".to_string());
        }

        // Common patterns check
        if !contains_common_patterns(password) {
            score += 1;
        } else {
            feedback.push("Password should not contain common patterns".to_string());
        }

        let strength_level = match score {
            0..=2 => StrengthLevel::Weak,
            3..=4 => StrengthLevel::Fair,
            5..=6 => StrengthLevel::Good,
            _ => StrengthLevel::Strong,
        };

        PasswordStrength {
            level: strength_level,
            score,
            feedback,
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum StrengthLevel {
    Weak,
    Fair,
    Good,
    Strong,
}

#[derive(Debug, Clone)]
pub struct PasswordStrength {
    pub level: StrengthLevel,
    pub score: u8,
    pub feedback: Vec<String>,
}

fn contains_common_patterns(password: &str) -> bool {
    let common_patterns = [
        "123456", "password", "qwerty", "abc123", "admin", "letmein",
        "welcome", "monkey", "1234567890", "password123", "admin123",
    ];

    let lower_password = password.to_lowercase();
    
    for pattern in &common_patterns {
        if lower_password.contains(pattern) {
            return true;
        }
    }

    // Check for simple sequences
    if has_sequence(&lower_password, 4) {
        return true;
    }

    // Check for repeated characters
    if has_repeated_chars(&lower_password, 3) {
        return true;
    }

    false
}

fn has_sequence(password: &str, min_length: usize) -> bool {
    let chars: Vec<char> = password.chars().collect();
    
    for i in 0..chars.len().saturating_sub(min_length - 1) {
        let mut is_sequence = true;
        
        for j in 1..min_length {
            if chars[i + j] as u8 != chars[i + j - 1] as u8 + 1 {
                is_sequence = false;
                break;
            }
        }
        
        if is_sequence {
            return true;
        }
    }
    
    false
}

fn has_repeated_chars(password: &str, min_count: usize) -> bool {
    let chars: Vec<char> = password.chars().collect();
    
    for i in 0..chars.len().saturating_sub(min_count - 1) {
        let mut count = 1;
        
        for j in i + 1..chars.len() {
            if chars[j] == chars[i] {
                count += 1;
                if count >= min_count {
                    return true;
                }
            } else {
                break;
            }
        }
    }
    
    false
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_password_hashing() {
        let manager = PasswordManager::new();
        let password = "test_password_123";
        
        let hash = manager.hash_password(password).unwrap();
        assert!(manager.verify_password(password, &hash).unwrap());
        assert!(!manager.verify_password("wrong_password", &hash).unwrap());
    }

    #[test]
    fn test_generate_secure_password() {
        let password = PasswordManager::generate_secure_password(16);
        assert_eq!(password.len(), 16);
        
        // Check that it contains different character types
        let strength = PasswordManager::check_password_strength(&password);
        assert!(matches!(strength.level, StrengthLevel::Good | StrengthLevel::Strong));
    }

    #[test]
    fn test_password_strength_weak() {
        let strength = PasswordManager::check_password_strength("123");
        assert_eq!(strength.level, StrengthLevel::Weak);
        assert!(!strength.feedback.is_empty());
    }

    #[test]
    fn test_password_strength_strong() {
        let strength = PasswordManager::check_password_strength("MyStr0ng!P@ssw0rd");
        assert!(matches!(strength.level, StrengthLevel::Good | StrengthLevel::Strong));
    }

    #[test]
    fn test_common_patterns() {
        assert!(contains_common_patterns("password123"));
        assert!(contains_common_patterns("admin"));
        assert!(contains_common_patterns("abcdef"));
        assert!(contains_common_patterns("aaaa"));
        assert!(!contains_common_patterns("MyStr0ng!P@ssw0rd"));
    }

    #[test]
    fn test_has_sequence() {
        assert!(has_sequence("abcdef", 4));
        assert!(has_sequence("123456", 4));
        assert!(!has_sequence("abcxyz", 4));
    }

    #[test]
    fn test_has_repeated_chars() {
        assert!(has_repeated_chars("aaaa", 3));
        assert!(has_repeated_chars("password111", 3));
        assert!(!has_repeated_chars("password", 3));
    }
}